#light
namespace FSharp.WebTools.ClientLibrary

#nowarn "44" // don't use webbehavior

open System.Collections
open System.Collections.Generic

open FSharp.WebTools
open FSharp.WebTools.Core

(*-----------------------------------------------------------------------------------------------*)
// JavaScript Events

[<ClientSide>]
type EventArgs = class
  [<ReflectedDefinition>]
  new () = { }
end

[<ClientSide>]
type ClientEvent = class
  [<Mapping("createEvent", MappingScope.Global, MappingType.Method)>]
  new() = {}  
  [<AttachEventHandler>]
  member t.Add(meth:(obj * #EventArgs -> unit)) = (raise ClientSideScript:unit);
  [<AttachEventHandler>]
  member t.AddClient(meth:(obj * #EventArgs -> #IClient<unit>)) = (raise ClientSideScript:unit);  
  [<Mapping("invoke", MappingScope.Member, MappingType.Method)>]
  member t.Invoke(sender:#obj, e:#EventArgs) = (raise ClientSideScript:unit);
end

(*-----------------------------------------------------------------------------------------------*)
namespace FSharp.WebTools.ClientLibrary.Internal
(*-----------------------------------------------------------------------------------------------*)
// Mappings for F# & .NET types that are not used directly by user code

open System
open Microsoft.FSharp
open System.Collections
open System.Collections.Generic

open FSharp.WebTools
open FSharp.WebTools.ClientLibrary

(*-----------------------------------------------------------------------------------------------*)
// Access to native JS methods

[<ClientSide>]
type DateTimeJS = class
  [<Mapping("Date", MappingScope.Global, MappingType.Object)>]
  new() = {}  
  [<Mapping("getMinutes", MappingScope.Member, MappingType.Method)>]
  member this.Minute with get () : int = (raise ClientSideScript:int)  
  [<Mapping("getSeconds", MappingScope.Member, MappingType.Method)>]
  member this.Second with get () : int = (raise ClientSideScript:int)  
  [<Mapping("getHours", MappingScope.Member, MappingType.Method)>]
  member this.Hour with get () : int = (raise ClientSideScript:int)  
  [<Mapping("getFullYear", MappingScope.Member, MappingType.Method)>]
  member this.Year with get () : int = (raise ClientSideScript:int)  
  [<Mapping("getMonth", MappingScope.Member, MappingType.Method)>]
  member this.Month with get () : int = (raise ClientSideScript:int)  
  [<Mapping("getDate", MappingScope.Member, MappingType.Method)>]
  member this.Day with get () : int = (raise ClientSideScript:int)
  [<Mapping("Lib.Utils.Subtract", MappingScope.Global, MappingType.Method)>]
  static member Subtract (a:DateTimeJS, b:DateTimeJS) : int =
    (raise ClientSideScript:int)
end
  
[<ClientSide>]  
type ArrayJS = class 
  [<Mapping("removeAt", MappingScope.Member, MappingType.Method)>]
  static member RemoveAt (arr:'a[], i:int) : unit = (raise ClientSideScript:unit)  
  [<Mapping("push", MappingScope.Member, MappingType.Method)>]
  static member Add (arr:'a[], v:'a) : unit = (raise ClientSideScript:unit)
  [<Mapping("join", MappingScope.Member, MappingType.Method)>]
  static member Join (arr:'b[], sep:'b) : 'b = (raise ClientSideScript:'b)
end

[<ClientSide>]
type XmlHttpRequestNative = class
  [<Mapping("responseText", MappingScope.Member, MappingType.Field)>]
  member this.ResponseText : string = (raise ClientSideScript)
  [<Mapping("readyState", MappingScope.Member, MappingType.Field)>]
  member this.ReadyState : int = (raise ClientSideScript)
  [<Mapping("status", MappingScope.Member, MappingType.Field)>]
  member this.Status : int = (raise ClientSideScript)    
  [<Mapping("onreadystatechange", MappingScope.Member, MappingType.Field)>]
  member this.ReadyStateChange 
    with get() = (raise ClientSideScript : unit -> unit)
    and set(v:unit -> unit) = (raise ClientSideScript : unit)
  [<Mapping("open", MappingScope.Member, MappingType.Method)>]
  member this.Open(meth:string, url:string, b:bool) : unit = (raise ClientSideScript)
  [<Mapping("send", MappingScope.Member, MappingType.Method)>]
  member this.Send(s:string) : unit = (raise ClientSideScript)
end

[<ClientSide>]
type ClientCore = class
  [<Mapping("Lib.Utils.TryCatch", MappingScope.Global, MappingType.Method)>]
  static member TryCatch(f:unit -> #IClient<'a>, err:unit -> #IClient<'a>) : ClientMonad<'a> = (raise ClientSideScript)
  [<Mapping("createGenericHandler", MappingScope.Global, MappingType.Method)>]
  static member CreateGenericHandler(e:ClientEvent) : unit -> unit = (raise ClientSideScript)
  [<Mapping("setInterval", MappingScope.Global, MappingType.Method)>]
  static member SetInterval(func:unit -> unit, i:int) : int = (raise ClientSideScript)
  [<Mapping("clearInterval", MappingScope.Global, MappingType.Method)>]
  static member ClearInterval(id:int) : unit = (raise ClientSideScript)
  [<Mapping("new ActiveXObject(\"Microsoft.XMLHTTP\")", MappingScope.Global, MappingType.Inline)>]
  static member CreateXmlHttpRequestAX() : XmlHttpRequestNative = (raise ClientSideScript)
  [<Mapping("XMLHttpRequest", MappingScope.Global, MappingType.Object)>]
  static member CreateXmlHttpRequestNative() : XmlHttpRequestNative = (raise ClientSideScript)
  [<Mapping("(window.XMLHttpRequest != null)", MappingScope.Global, MappingType.Inline)>]
  static member SupportsNativeXmlHttp : bool = (raise ClientSideScript)
  [<Mapping("eval", MappingScope.Global, MappingType.Method)>]
  static member Eval<'a> (code:string) : 'a = (raise ClientSideScript)
  [<Mapping("Lib.Utils.GetMember", MappingScope.Global, MappingType.Method)>]
  static member GetMember (o:#obj, memb:string) : 'a = (raise ClientSideScript)
  [<Mapping("Lib.Utils.SetMember", MappingScope.Global, MappingType.Method)>]
  static member SetMember (o:#obj, memb:string, v:#obj) : unit = (raise ClientSideScript)
  [<Mapping("Lib.Utils.InvokeMember", MappingScope.Global, MappingType.Method)>]
  static member InvokeMember (o:obj, memb:string, args:obj array) : obj = (raise ClientSideScript)
  [<Mapping("Lib.Utils.InvokeStatic", MappingScope.Global, MappingType.Method)>]
  static member InvokeStatic (memb:string, args:obj array) : obj = (raise ClientSideScript)
  [<Mapping("typeof", MappingScope.Global, MappingType.Method)>]
  static member TypeName (o:#obj) : string = (raise ClientSideScript)
  [<Mapping("null", MappingScope.Global, MappingType.Inline)>]
  static member Null () : 'a = (raise ClientSideScript)
  [<Mapping("Lib.Utils.FailWith", MappingScope.Global, MappingType.Method)>]
  static member FailWith(s:string) : 'a = (raise ClientSideScript)
  [<Mapping("Lib.Utils.Ignore", MappingScope.Global, MappingType.Method)>]
  static member Ignore(s:string) : 'a = (raise ClientSideScript)
  [<Mapping("Lib.Utils.Cast", MappingScope.Global, MappingType.Method)>]
  static member Cast<'a>(o:obj) : 'a = (raise ClientSideScript)
  [<Mapping("Lib.Utils.HasValue", MappingScope.Global, MappingType.Method)>]
  static member HasValue<'a>(o:'a) : bool = (raise ClientSideScript)
  [<Mapping("Lib.Utils.MembersIter", MappingScope.Global, MappingType.Method)>]
  static member MembersIter(o:#obj, f:string -> unit) : unit = (raise ClientSideScript)
  [<Mapping("Lib.Convert.ToString", MappingScope.Global, MappingType.Method)>]
  static member ToString<'a>(o:'a) : string = (raise ClientSideScript)
  [<Mapping("Lib.Utils.Not", MappingScope.Global, MappingType.Method)>]
  static member Not(a:bool) : bool = (raise ClientSideScript)
  [<Mapping("Lib.Utils.GetArguments(arguments)", MappingScope.Global, MappingType.Inline)>]
  static member GetArguments() : obj[] = (raise ClientSideScript)
end

[<ClientSide>]
type ConvertModule = class
  [<Mapping("Lib.Convert.ToString", MappingScope.Global, MappingType.Method)>]
  static member ToString<'a>(o:'a) : string = (raise ClientSideScript)
  [<Mapping("Lib.Convert.ToFloat", MappingScope.Global, MappingType.Method)>]
  static member ToFloat<'a>(o:'a) : float = (raise ClientSideScript)
  [<Mapping("Lib.Convert.ToInt", MappingScope.Global, MappingType.Method)>]
  static member ToInt<'a>(o:'a) : int = (raise ClientSideScript)
end

(*-----------------------------------------------------------------------------------------------*)
// F# types - tuple & discriminated union (records don't need any support type
(*-----------------------------------------------------------------------------------------------*)

[<NeutralSide>]
type Tuple = class
  val mutable args : obj[]
  val mutable __net_type__ : string
  val mutable __js_special__ : string

  [<ClientIgnored>]      
  new() = {
    args = Array.zero_create 0; 
    __js_special__ = "tuple";
    __net_type__ = "" }
  
  [<ReflectedDefinition>]
  new(args:obj[], typ) =
    let largs = Array.zero_create (args.Length)
    for i = 0 to args.Length - 1 do // this is some odd workaround (but it may not be needed now)
      largs.[i] <- args.[i]; 
    { args = largs; 
      __js_special__ = "tuple";
      __net_type__ = typ }
  
  [<ReflectedDefinition>]
  member this.Get(idx) = 
    this.args.[idx];
end

[<NeutralSide>]
type DiscriminatedUnion = class
  val mutable name : string
  val mutable args : obj[]
  val mutable __net_type__ : string
  val mutable __js_special__ : string
  
  [<ClientIgnored>]      
  new() = 
    { name = ""; 
      args = Array.zero_create 0; 
      __net_type__ = "";
      __js_special__ = "union"; }
  
  [<ReflectedDefinition>]
  new(name, args:obj[], typ) =
    let largs = Array.zero_create (args.Length)
    for i = 0 to args.Length - 1 do
      largs.[i] <- args.[i]; 
    { args = largs; name = name; 
      __net_type__ = typ; __js_special__ = "union" }      
    
  [<ReflectedDefinition>]
  member this.IsTag(s) = 
    s = this.name;
   
  [<ReflectedDefinition>]  
  member this.Get(s, idx) =
    if (false = this.IsTag(s)) then
      failwith "Invalid access to discriminated union value!"; 
    else
      this.args.[idx];
end

(*-----------------------------------------------------------------------------------------------*)
// F# modules - modules with basic F# functions 

[<ClientSide>]
type Operators = class   
  [<Mapping("Lib.Utils.OpIndexedAssign", MappingScope.Global, MappingType.Method)>]
  static member IndexedAssign(o:#obj, idx:int, v:#obj) = (raise ClientSideScript:unit)
  [<Mapping("Lib.Utils.OpIndexedLookup", MappingScope.Global, MappingType.Method)>]
  static member IndexedLookup(o:#obj, idx:int) = (raise ClientSideScript:#obj)
end

[<ClientSide>]
type ListModule = class   
  [<ReflectedDefinition>]
  static member Append(l1:obj list, l2:obj list) =
    match l1 with 
    | [] -> l2
    | (h::t) -> h::(ListModule.Append(t,l2))

  [<ReflectedDefinition>]
  static member Map(f, l:obj list) =
    match l with 
    | [] -> []
    | (h::t) -> (f h)::(ListModule.Map(f,t))    

  [<ReflectedDefinition>]
  static member Iter(f, l:obj list) =
    match l with 
    | [] -> ();
    | (h::t) -> f h; ListModule.Iter(f,t);

  [<ReflectedDefinition>]
  static member FoldLeft(f, st:obj, l:obj list) =
    match l with 
    | (h::t) -> ListModule.FoldLeft(f, (f st h), t)
    | [] -> st

  [<ReflectedDefinition>]
  static member Filter(f, l:obj list) =
    match l with 
    | [] -> []
    | (h::t) -> 
        if (f h) then h::(ListModule.Filter(f,t))    
          else (ListModule.Filter(f,t))

  [<ReflectedDefinition>]
  static member Length(l:obj list) =
    match l with 
    | [] -> 0
    | (h::t) -> 1 + ListModule.Length(t)
end

[<ClientSide>]
type ArrayModule = class   
  [<Mapping("Lib.Utils.ArrayZeroCreate", MappingScope.Global, MappingType.Method)>]
  static member ZeroCreate(n:int, ty:string) : 'a[] = (raise ClientSideScript)

  [<ReflectedDefinition>]
  static member Iter(f:'a -> 'b, l:'a[]) =
    for i = 0 to l.Length - 1 do
     f (l.[i])

  [<ReflectedDefinition>]
  static member IterI(f:(int*'a) -> unit, l:'a[]) =
    for i = 0 to l.Length - 1 do
     f (i, l.[i])

  [<ReflectedDefinition>]
  static member Map(f, l:obj[]) =
    let res = Array.zero_create (l.Length)
    for i = 0 to l.Length - 1 do
      res.[i] <- f (l.[i])
    res
    
  [<ReflectedDefinition>]
  static member FoldLeft(f, st:obj, l:obj[]) =
    let res = ref st
    for i = 0 to l.Length - 1 do
      res := f (!res) (l.[i])
    (!res)
    
  [<ReflectedDefinition>]
  static member Filter(f, l:obj[]) =
    let res = new ResizeArray<_>() 
    for i = 0 to l.Length - 1 do 
        if f (l.[i]) then res.Add(l.[i])
    res.ToArray()

  [<ReflectedDefinition>]
  static member Exists(f, l:obj[]) =
    let rec exists i =
      if (i = l.Length) then false 
      elif (f l.[i]) then true
      else exists (i+1)
    exists 0
end

[<ClientSide>]
type CharModule = class
  [<Mapping("charCodeAt(0)", MappingScope.Member, MappingType.Inline)>]
  static member Code(c:char) : int = (raise ClientSideScript)

  [<Mapping("String.fromCharCode", MappingScope.Global, MappingType.Method)>]
  static member Chr(i:int) : char = (raise ClientSideScript)
end  

type SFormat = Format of string

[<ClientSide>]
type PrintModule = class
  [<ReflectedDefinition>]
  static member Sprintf() =
    let args = ClientCore.GetArguments();
    let (Format fmt) = ClientCore.Cast<SFormat>(args.[0]);
    let fca = fmt.ToCharArray();

    let rec fmt acc idx argidx =
      if (idx < fca.Length) then
        let idx, narg, append = 
          if (fca.[idx] = '%') then
            idx+2, argidx+1, ClientCore.ToString(args.[argidx])
          else
            idx+1, argidx, fca.[idx].ToString()    
        fmt (acc+append) idx narg
      else 
        acc

    fmt "" 0 1
end  

(*-----------------------------------------------------------------------------------------------*)
// .NET types - System namespace

[<ClientSide; ExternalType(type System.Array)>]
type Array = class
  [<Mapping("length", MappingScope.Member, MappingType.Field)>]
  member x.Length : int = (raise ClientSideScript:int)
end

[<ClientSide; ExternalType(type System.Object)>]
type Object = class
  [<Mapping("Lib.Convert.ToString", MappingScope.Global, MappingType.Method)>]
  override x.ToString() : string = (raise ClientSideScript)
end  

[<ClientSide; ExternalType(type System.Char)>]
type Char = class
  [<Mapping("Lib.Convert.ToString", MappingScope.Global, MappingType.Method)>]
  override x.ToString() : string = (raise ClientSideScript)
end  

[<ClientSide; ExternalType(type System.Int32)>]
type Int32 = class
  [<Mapping("Lib.Convert.ToString", MappingScope.Global, MappingType.Method)>]
  override x.ToString() : string = (raise ClientSideScript)

  [<ReflectedDefinition>]
  static member Parse(s:string) =
    let ca = s.ToCharArray()
    let zcd = Char.code('0')
    if (ca.Length = 0) then failwith "Invalid string representation of an integer!"
    for i = 1 to ca.Length - 1 do 
      if (ca.[i] < '0' || ca.[i] > '9') then failwith "Invalid string representation of an integer!"
    let m = if (ca.[0] = '-') then -1 else 1
    m * Array.fold_left (fun n c -> 
          if (c = '-') then n else
          (10*n) + (Char.code(c) - zcd)) 0 ca
end  

[<ClientSide; ExternalType(type System.Double)>]
type Double = class
  [<Mapping("Lib.Convert.ToString", MappingScope.Global, MappingType.Method)>]
  override x.ToString() : string = (raise ClientSideScript)
  
  [<ReflectedDefinition>]
  static member Parse(n:string) =
    let idd = n.IndexOf('.')
    if (idd = -1) then
      float (Int32.Parse(n))
    else
      let num = n.Substring(0, idd)
      let dec = n.Substring(idd+1, n.Length-1-idd)
      if (dec.IndexOf('.') <> -1) then failwith "Invalid string representation of a double!"
      let idec = Int32.Parse(dec)
      if (idec < 0) then failwith "Invalid string representation of a double!"
      (float (Int32.Parse(num))) + (float (idec)/Math.Pow(10.0, float (n.Length - 1 - idd)))
end  

[<ClientSide; ExternalType(type System.String)>]
type StringJS = class
  [<Mapping("charAt", MappingScope.Member, MappingType.Method)>]
  member x.Item with get(i:int) : char = (raise ClientSideScript)  
  [<Mapping("substr", MappingScope.Member, MappingType.Method)>]
  member x.Substring(from:int, length:int) : string = (raise ClientSideScript)  
  [<Mapping("substr", MappingScope.Member, MappingType.Method)>]
  member x.Substring(from:int) : string = (raise ClientSideScript)  
  [<Mapping("indexOf", MappingScope.Member, MappingType.Method)>]
  member x.IndexOf(find:string) : int = (raise ClientSideScript)  
  [<Mapping("startsWith", MappingScope.Member, MappingType.Method)>]
  member x.StartsWith(find:string) : bool = (raise ClientSideScript)  
  [<Mapping("replaceAll", MappingScope.Member, MappingType.Method)>]
  member x.Replace(str:string, repl:string) : string = (raise ClientSideScript)  
  [<Mapping("toCharArray", MappingScope.Member, MappingType.Method)>]
  member x.ToCharArray() : char[] = (raise ClientSideScript)  
  [<Mapping("length", MappingScope.Member, MappingType.Field)>]
  member x.Length : int = (raise ClientSideScript:int)

  [<Mapping("StringJS.SplitByCharArray", MappingScope.Global, MappingType.Method)>]
  member x.Split(by:char[]) : string[] = (raise ClientSideScript)

  [<ReflectedDefinition>]
  static member SplitByCharArray(s:string, by:char[]) : string[] = 
    let res = [| |]:string[]
    let ca = s.ToCharArray()
    let s = ref ""
    for i = 0 to ca.Length-1 do
      let c = ca.[i]
      let ex = Array.exists (fun b -> b = c) by
      if (ex) then
        ArrayJS.Add(res, !s)
        s := ""
      else
        s := (!s) + c.ToString()
    ArrayJS.Add(res, !s)
    res
end;

[<ClientSide; ExternalType(type System.TimeSpan)>] 
type TimeSpan = class
  val msecs : int 
  
  [<ReflectedDefinition>]
  new(ms) = { msecs = ms; }
  [<ReflectedDefinition>]
  member this.TotalMilliseconds = 
    (this.msecs)    
  [<ReflectedDefinition>]
  member this.TotalSeconds = 
    int (this.msecs / 1000)    
  [<ReflectedDefinition>]
  member this.TotalMinutes = 
    int (this.msecs / (1000*60))    
  [<ReflectedDefinition>]
  member this.TotalHours = 
    int (this.msecs / (1000*60*60))    
  [<ReflectedDefinition>]
  member this.TotalDays = 
    int (this.msecs / (1000*60*60*24)) 
    
  [<ClientIgnored>]    
  member t.ToExternalType() =
    new System.TimeSpan(int64 t.msecs);
  
  [<ClientIgnored>]  
  static member FromExternalType(t:System.TimeSpan) =
    new TimeSpan(int t.TotalMilliseconds);
end
  
[<ClientSide; ExternalType(type System.DateTime)>] 
type DateTime = class  
  val mutable day : int
  val mutable month : int
  val mutable year : int 
  val mutable minute : int
  val mutable hour : int
  val mutable second : int
  val mutable millisecond : int

  [<ReflectedDefinition>]  
  new(y,m,d,h,mi,s,ms) = 
    { day=d; month=m; year=y; minute=mi;
      second=s; hour=h; millisecond=ms; }
          
  [<ReflectedDefinition>]
  static member Now = 
    let dt = new DateTimeJS() 
    new DateTime(dt.Year, dt.Month+1, dt.Day, dt.Hour, dt.Minute, dt.Second, 0) 
            
  [<ReflectedDefinition>]  
  member this.Minute = this.minute  
  [<ReflectedDefinition>]  
  member this.Second = this.second
  [<ReflectedDefinition>]  
  member this.Millisecond = this.millisecond  
  [<ReflectedDefinition>]  
  member this.Hour = this.hour  
  [<ReflectedDefinition>]  
  member this.Month = this.month  
  [<ReflectedDefinition>]  
  member this.Year = this.year  
  [<ReflectedDefinition>]  
  member this.Day = this.day
  
  [<ReflectedDefinition>]  
  static member TryParse(s) = 
    failwith "Parsing not implemented!"

//  [<ReflectedDefinition>]  
//  override this.ToString() = 
//    this.ToString("dd/MM/y hh:mm:ss")

  [<ReflectedDefinition>]  
  member this.ToString(sfmt:string) =  
    let sfmt = if (ClientCore.HasValue(sfmt)) then sfmt else "hh:mm:ss dd/MM/y" // may be null when called like .ToString() !! workaround !!
    let h = this.Hour.ToString()
    let m = this.Minute.ToString()
    let s = this.Second.ToString()
    let d = this.Day.ToString()
    let n = this.Month.ToString()
    let y = this.Year.ToString()
    let ca = sfmt.ToCharArray();
    
    let rec addZeros(n,s:string) = 
      if (s.Length < n) then addZeros(n, "0"+s) else s;        
    let lastChars(n,s:string) = 
      s.Substring(s.Length-n, n)
      
    let rec testMatch (readFunc:int->char option) i remChars = 
      match remChars, readFunc(i) with
      | c::r, Some(c1) when c = c1 -> testMatch readFunc (i+1) r
      | [], _ -> true
      | _ -> false
      
    let rec findFnc(readFunc:int->char option, l) : ((unit->string)*int) option =
      match l with 
      | (chars,f)::xs -> 
          if (testMatch readFunc 0 chars) then 
            Some(f, List.length chars)
          else
            findFnc(readFunc, xs)
      | [] -> None        
    let fncs = 
      [ ['h'; 'h'], (fun () -> addZeros(2,h));  ['h'], (fun () -> h); // hh; h
        ['m'; 'm'], (fun () -> addZeros(2,m));  ['m'], (fun () -> m); // mm; m
        ['s'; 's'], (fun () -> addZeros(2,s));  ['s'], (fun () -> s); // ss; s
        ['d'; 'd'], (fun () -> addZeros(2,d));  ['d'], (fun () -> d); // dd; d
        ['M'; 'M'], (fun () -> addZeros(2,n));  ['M'], (fun () -> n); // MM; M
        ['y'; 'y'; 'y'; 'y'], (fun () -> y);
        ['y'; 'y'], (fun () -> lastChars(2,y)); ['y'], (fun () -> lastChars(1,y)); ] // yy; y
    let rec format i =
      if (i < ca.Length) then
        let readFunc = (fun ofs -> if (ofs+i >= ca.Length) then None else Some(ca.[ofs+i]))
        let (app,ni) =
          match findFnc(readFunc, fncs) with
          | Some (f,n) -> (f (), i+n)
          | _ -> (ca.[i].ToString(), i+1)
        app + (format(ni))
      else ""  
    format(0)

  [<ReflectedDefinition>]  
  member this.Ticks = 
    ((((((((this.year * 12 + this.month) * 30) + this.day) * 24) +
            this.hour) * 60 + this.minute) * 60 + this.second) * 1000) + 
            this.millisecond  // not completely correct..

  [<ReflectedDefinition>]  
  member this.Subtract(other:DateTime) = 
    let tt = this.Ticks
    let ot = other.Ticks
    new TimeSpan(tt - ot)

  [<ClientIgnored>]
  new() = { day=0; month=0; year=0; minute=0; 
            second=0; hour=0; millisecond=0; }
      
  [<ClientIgnored>]
  member t.ToExternalType() =
    new System.DateTime
      (t.year, t.month, t.day, t.hour,
       t.minute, t.second, t.millisecond)

  [<ClientIgnored>]    
  static member FromExternalType(t:System.DateTime) =
    new DateTime
      (t.Year, t.Month, t.Day, t.Hour,
       t.Minute, t.Second, t.Millisecond)
end

[<ClientSide; ExternalType(type System.Random)>]
type Random = class
  [<Mapping("Math.random", MappingScope.Global, MappingType.Method)>]
  static member RandomJS() : float = (raise ClientSideScript)

  [<ReflectedDefinition>]
  new() = {}
  
  [<ReflectedDefinition>]
  member x.Next() =
    let f = Random.RandomJS()
    int (f * 2147483647.0)

  [<ReflectedDefinition>]
  member x.Next(n:int) =
    let f = Random.RandomJS()
    int (f * (float n))    
end

[<ClientSide;ExternalType(type System.Math)>]
type MathJS = class
  [<Mapping("Math.pow", MappingScope.Global, MappingType.Method)>]
  static member Pow(d:float, exp:float) : float = (raise ClientSideScript)
  [<Mapping("Math.sqrt", MappingScope.Global, MappingType.Method)>]
  static member Sqrt(d:float) : float = (raise ClientSideScript)
  [<Mapping("Math.cos", MappingScope.Global, MappingType.Method)>]
  static member Cos(d:float) : float = (raise ClientSideScript)
  [<Mapping("Math.sin", MappingScope.Global, MappingType.Method)>]
  static member Sin(d:float) : float = (raise ClientSideScript)
end  

(*-----------------------------------------------------------------------------------------------*)
// .NET types - System.Collections namespace

[<ClientSide; ExternalType(type System.Collections.Generic.List<obj>)>]
type ResizeArray<'a> = class
  val mutable array : 'a[]; 

  [<ReflectedDefinition>]
  new() = { array = Array.zero_create 0; }

  [<ReflectedDefinition>]
  member this.Contains(el:'a) = 
    let res = ref false;
    for i = 0 to this.array.Length - 1 do
      if (this.[i] = el) then res := true;
    (!res)

  [<ReflectedDefinition>]
  member this.Add(el:'a) = 
    ArrayJS.Add(this.array, el);

  [<ReflectedDefinition>]
  member this.AddRange(r:'a[]) = // TODO: only array .. ?
    for i=0 to r.Length-1 do
      ArrayJS.Add(this.array, r.[i]);

  [<ReflectedDefinition>]
  member this.RemoveAt(i:int) = 
    ArrayJS.RemoveAt(this.array, i);
      
  [<ReflectedDefinition>]
  member this.Item 
    with get(i:int) : 'a = this.array.[i];
    and  set(i:int,v:'a) : unit = this.array.[i] <- v;

  [<ReflectedDefinition>]
  member this.Count 
    with get() = this.array.Length;

  [<ReflectedDefinition>]
  member this.ToArray() =
    let ar = Array.zero_create (this.Count)
    for i = 0 to ar.Length - 1 do
      ar.[i] <- this.[i];
    ar
    
  [<ClientIgnored>]
  member t.ToExternalType() =
    let r = new Microsoft.FSharp.Collections.ResizeArray<'ta>();
    t.array |> Array.iter (r.Add)
    r
  
  [<ClientIgnored>]  
  static member FromExternalType(res:Microsoft.FSharp.Collections.ResizeArray<'a>) =
    let r = new ResizeArray<'a>()
    r.array <- res.ToArray()
    r   
end
  
[<ClientSide; ExternalType(type System.Collections.Generic.Stack<obj>)>]
type Stack<'a> = class

  val lst : ResizeArray<'a>
  [<ReflectedDefinition>]
  new () = { lst = new ResizeArray<'a>(); }

  [<ReflectedDefinition>]
  member this.Clear() : unit =
    failwith "Not implemented!"
  
  [<ReflectedDefinition>]
  member this.Contains(v:'a) : bool =
    failwith "Not implemented!"
  
  [<ReflectedDefinition>]
  member this.Push(v:'a) =
    this.lst.Add(v);
  
  [<ReflectedDefinition>]
  member this.Pop() =
    if (this.Count = 0) then
      failwith "Can't pop, stack is empty!"
    else
      let r = this.lst.[this.lst.Count - 1]; 
      this.lst.RemoveAt(this.lst.Count - 1);
      r;
  
  [<ReflectedDefinition>]
  member this.Peek() =
    if (this.Count = 0) then
      failwith "Can't peek, stack is empty!"
    else
      this.lst.[this.lst.Count - 1]; 

  [<ReflectedDefinition>]
  member this.Count = 
    this.lst.Count

  [<ClientIgnored>]
  member t.ToExternalType() =
    let r = new System.Collections.Generic.Stack<'a>()
    for i = 0  to t.lst.Count do r.Push(t.lst.[i]);
    r
  
  [<ClientIgnored>]  
  static member FromExternalType(res:System.Collections.Generic.Stack<'a>) =
    let r = new Stack<'a>()
    res.ToArray() |> Array.iter (r.Push)
    r
end


(*-----------------------------------------------------------------------------------------------*)
// .NET types - System.Text namespace

[<ClientSide; ExternalType(type System.Text.StringBuilder)>]
type StringBuilder = class  
  val strings : string[]
  
  [<ReflectedDefinition>]
  new () = { strings = Array.zero_create 0; }

  [<ReflectedDefinition>]
  member this.Append(s:string) =
    ArrayJS.Add(this.strings, s);
    this
  
  [<ReflectedDefinition>]
  member this.ToString() =
    ArrayJS.Join(this.strings, ""); 
end

(*-----------------------------------------------------------------------------------------------*)
// .NET types - System.IO namespace

[<ClientSide; ExternalType(type System.IO.TextWriter)>]
type TextWriter = class
  [<ReflectedDefinition>]
  new () = {}
  
  [<ReflectedDefinition>]
  abstract Write : string -> unit;
end

[<ClientSide; ExternalType(type System.IO.StringWriter)>]
type StringWriter = class  
  inherit TextWriter
  val sb : StringBuilder
  
  [<ReflectedDefinition>]
  new (s) = { sb = s; }

  [<ReflectedDefinition>]
  override this.Write(s:string) =
    ignore(this.sb.Append(s));
end

(*-----------------------------------------------------------------------------------------------*)
// .NET types - System.Web.UI namespace

[<ClientSide; ExternalType(type System.Web.UI.HtmlTextWriter)>]
type HtmlTextWriter = class
  inherit TextWriter
  
  val sw : TextWriter
  
  [<ReflectedDefinition>]
  new (w) = { sw = w; }

  [<ReflectedDefinition>]
  override this.Write(s:string) =
    this.sw.Write(s);
end
